using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SolarWinds.Cameras;

namespace SolarWinds.Effects
{
    public class SkyBox : DrawableGameComponent
    {
        #region Fields

        private readonly string[] mAssets = new string[6];
        private readonly Texture2D[] mTextures = new Texture2D[6];
        private Effect mEffect;

        private VertexBuffer mVertexBuffer;
        private IndexBuffer mIndexBuffer;
        private VertexDeclaration mVertexDecl;

        #endregion

        #region Properties

        public string AssetBack
        {
            get { return mAssets[0]; }
            set { mAssets[0] = value; }
        }

        public string AssetFront
        {
            get { return mAssets[1]; }
            set { mAssets[1] = value; }
        }

        public string AssetBottom
        {
            get { return mAssets[2]; }
            set { mAssets[2] = value; }
        }

        public string AssetTop
        {
            get { return mAssets[3]; }
            set { mAssets[3] = value; }
        }

        public string AssetLeft
        {
            get { return mAssets[4]; }
            set { mAssets[4] = value; }
        }

        public string AssetRight
        {
            get { return mAssets[5]; }
            set { mAssets[5] = value; }
        }

        #endregion

        public SkyBox(Microsoft.Xna.Framework.Game game)
            : base(game)
        {
        }

        StateBlock mBlock;

        public override void Draw(GameTime gameTime)
        {
            mBlock.Capture();

            if (mVertexBuffer == null)
                return;

            Camera cam = SolarWindsGame.Camera;

            mEffect.Begin();
            mEffect.Parameters["worldViewProjection"].SetValue(
                Matrix.CreateTranslation(cam.Position) *
                cam.View * cam.Projection);

            for (int x = 0; x < 6; x++)
            {
                /* TODO get some speed by culling planes
                 * float f = 0;
                switch (x)
                {
                    case 0: //back
                        f = Vector3.Dot(mCamera, new Vector3(0, 0, 1));
                        break;
                    case 1: //front
                        f = Vector3.Dot(vCameraDirection, new Vector3(0, 0, -1));
                        break;
                    case 2: //bottom
                        f = Vector3.Dot(vCameraDirection, new Vector3(0, 1, 0));
                        break;
                    case 3: //top
                        f = Vector3.Dot(vCameraDirection, new Vector3(0, -1, 0));
                        break;
                    case 4: //left
                        f = Vector3.Dot(vCameraDirection, new Vector3(1, 0, 0));
                        break;
                    case 5: //right
                        f = Vector3.Dot(vCameraDirection, new Vector3(-1, 0, 0));
                        break;
                }*/

                //if (f <= 0)
                {
                    var graphicsService = (IGraphicsDeviceService)Game.Services.GetService(typeof(IGraphicsDeviceService));

                    GraphicsDevice device = graphicsService.GraphicsDevice;
                    device.VertexDeclaration = mVertexDecl;
                    device.Vertices[0].SetSource(mVertexBuffer, 0,
                                                 mVertexDecl.GetVertexStrideSize(0));

                    device.Indices = mIndexBuffer;

                    mEffect.Parameters["baseTexture"].SetValue(mTextures[x]);
                    mEffect.Techniques[0].Passes[0].Begin();

                    device.DrawIndexedPrimitives(PrimitiveType.TriangleList,
                                                 0, x * 4, 4, x * 6, 2);
                    mEffect.Techniques[0].Passes[0].End();
                }
            }

            mEffect.End();

            mBlock.Apply();
        }

        protected override void LoadContent()
        {
            base.LoadContent();

            GraphicsDevice device = GraphicsDevice;

            mBlock = new StateBlock(device);

            for (int i = 0; i < 6; ++i)
            {
                mTextures[i] = Game.Content.Load<Texture2D>(@"Textures\" + mAssets[i]);
            }

            mEffect = Game.Content.Load<Effect>(@"Shaders\skybox");

            mVertexDecl = new VertexDeclaration(device,
                                                new VertexElement[] {
                                                                        new VertexElement(0,0,VertexElementFormat.Vector3,
                                                                                          VertexElementMethod.Default,
                                                                                          VertexElementUsage.Position,0),
                                                                        new VertexElement(0,sizeof(float)*3,VertexElementFormat.Vector2,
                                                                                          VertexElementMethod.Default,
                                                                                          VertexElementUsage.TextureCoordinate,0)});

            mVertexBuffer = new VertexBuffer(device,
                                             typeof(VertexPositionTexture),
                                             4 * 6,
                                             BufferUsage.WriteOnly);

            var data = new VertexPositionTexture[4 * 6];

            var vExtents = new Vector3(2500, 2500, 2500);
            //back
            data[0].Position = new Vector3(vExtents.X, -vExtents.Y, -vExtents.Z);
            data[0].TextureCoordinate.X = 1.0f; data[0].TextureCoordinate.Y = 1.0f;
            data[1].Position = new Vector3(vExtents.X, vExtents.Y, -vExtents.Z);
            data[1].TextureCoordinate.X = 1.0f; data[1].TextureCoordinate.Y = 0.0f;
            data[2].Position = new Vector3(-vExtents.X, vExtents.Y, -vExtents.Z);
            data[2].TextureCoordinate.X = 0.0f; data[2].TextureCoordinate.Y = 0.0f;
            data[3].Position = new Vector3(-vExtents.X, -vExtents.Y, -vExtents.Z);
            data[3].TextureCoordinate.X = 0.0f; data[3].TextureCoordinate.Y = 1.0f;

            //front
            data[4].Position = new Vector3(-vExtents.X, -vExtents.Y, vExtents.Z);
            data[4].TextureCoordinate.X = 1.0f; data[4].TextureCoordinate.Y = 1.0f;
            data[5].Position = new Vector3(-vExtents.X, vExtents.Y, vExtents.Z);
            data[5].TextureCoordinate.X = 1.0f; data[5].TextureCoordinate.Y = 0.0f;
            data[6].Position = new Vector3(vExtents.X, vExtents.Y, vExtents.Z);
            data[6].TextureCoordinate.X = 0.0f; data[6].TextureCoordinate.Y = 0.0f;
            data[7].Position = new Vector3(vExtents.X, -vExtents.Y, vExtents.Z);
            data[7].TextureCoordinate.X = 0.0f; data[7].TextureCoordinate.Y = 1.0f;

            //bottom
            data[8].Position = new Vector3(-vExtents.X, -vExtents.Y, -vExtents.Z);
            data[8].TextureCoordinate.X = 1.0f; data[8].TextureCoordinate.Y = 0.0f;
            data[9].Position = new Vector3(-vExtents.X, -vExtents.Y, vExtents.Z);
            data[9].TextureCoordinate.X = 1.0f; data[9].TextureCoordinate.Y = 1.0f;
            data[10].Position = new Vector3(vExtents.X, -vExtents.Y, vExtents.Z);
            data[10].TextureCoordinate.X = 0.0f; data[10].TextureCoordinate.Y = 1.0f;
            data[11].Position = new Vector3(vExtents.X, -vExtents.Y, -vExtents.Z);
            data[11].TextureCoordinate.X = 0.0f; data[11].TextureCoordinate.Y = 0.0f;

            //top
            data[12].Position = new Vector3(vExtents.X, vExtents.Y, -vExtents.Z);
            data[12].TextureCoordinate.X = 0.0f; data[12].TextureCoordinate.Y = 0.0f;
            data[13].Position = new Vector3(vExtents.X, vExtents.Y, vExtents.Z);
            data[13].TextureCoordinate.X = 0.0f; data[13].TextureCoordinate.Y = 1.0f;
            data[14].Position = new Vector3(-vExtents.X, vExtents.Y, vExtents.Z);
            data[14].TextureCoordinate.X = 1.0f; data[14].TextureCoordinate.Y = 1.0f;
            data[15].Position = new Vector3(-vExtents.X, vExtents.Y, -vExtents.Z);
            data[15].TextureCoordinate.X = 1.0f; data[15].TextureCoordinate.Y = 0.0f;

            //left
            data[16].Position = new Vector3(-vExtents.X, vExtents.Y, -vExtents.Z);
            data[16].TextureCoordinate.X = 1.0f; data[16].TextureCoordinate.Y = 0.0f;
            data[17].Position = new Vector3(-vExtents.X, vExtents.Y, vExtents.Z);
            data[17].TextureCoordinate.X = 0.0f; data[17].TextureCoordinate.Y = 0.0f;
            data[18].Position = new Vector3(-vExtents.X, -vExtents.Y, vExtents.Z);
            data[18].TextureCoordinate.X = 0.0f; data[18].TextureCoordinate.Y = 1.0f;
            data[19].Position = new Vector3(-vExtents.X, -vExtents.Y, -vExtents.Z);
            data[19].TextureCoordinate.X = 1.0f; data[19].TextureCoordinate.Y = 1.0f;

            //right
            data[20].Position = new Vector3(vExtents.X, -vExtents.Y, -vExtents.Z);
            data[20].TextureCoordinate.X = 0.0f; data[20].TextureCoordinate.Y = 1.0f;
            data[21].Position = new Vector3(vExtents.X, -vExtents.Y, vExtents.Z);
            data[21].TextureCoordinate.X = 1.0f; data[21].TextureCoordinate.Y = 1.0f;
            data[22].Position = new Vector3(vExtents.X, vExtents.Y, vExtents.Z);
            data[22].TextureCoordinate.X = 1.0f; data[22].TextureCoordinate.Y = 0.0f;
            data[23].Position = new Vector3(vExtents.X, vExtents.Y, -vExtents.Z);
            data[23].TextureCoordinate.X = 0.0f; data[23].TextureCoordinate.Y = 0.0f;

            mVertexBuffer.SetData(data);

            mIndexBuffer = new IndexBuffer(device,
                                           typeof(short), 6 * 6,
                                           BufferUsage.WriteOnly);

            var ib = new short[6 * 6];

            for (int x = 0; x < 6; x++)
            {
                ib[x * 6 + 0] = (short)(x * 4 + 0);
                ib[x * 6 + 2] = (short)(x * 4 + 1);
                ib[x * 6 + 1] = (short)(x * 4 + 2);

                ib[x * 6 + 3] = (short)(x * 4 + 2);
                ib[x * 6 + 5] = (short)(x * 4 + 3);
                ib[x * 6 + 4] = (short)(x * 4 + 0);
            }

            mIndexBuffer.SetData(ib);
        }
    }
}